/*
	This file is part of AVRCppLib.

    AVRCppLib is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    AVRCppLib is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with AVRCppLib.  If not, see <http://www.gnu.org/licenses/>.

    Copyright (C) Maxime Lemonnier 2007-2011
 */

#include "ActiveBufferedUSART.h"
#include "ActiveBufferedUSARTFrameReceivedCallback.h"
#include "ActiveBufferedUSARTTransmitterReadyCallback.h"

namespace Coms
{

namespace USART
{

ActiveBufferedUSART::ActiveBufferedUSART(USARTHardware * usart) :
currentTransmitBuffer(NULL), currentReceiveBuffer(NULL), usart(usart)
{
	usart->connectFrameReceivedCallback(new ActiveBufferedUSARTFrameReceivedCallback(this));

	usart->connectTransmitterReadyCallback(new ActiveBufferedUSARTTransmitterReadyCallback(this));
}

ActiveBufferedUSART::~ActiveBufferedUSART()
{
	delete usart->disconnectFrameReceivedCallback();

	delete usart->disconnectTransmitterReadyCallback();
}

void ActiveBufferedUSART::waitReceiver()
{
	while(!usart->isFrameReceived() && currentReceiveBuffer->isOpen());
}


void ActiveBufferedUSART::readOperation()
{
	if(isNull(currentReceiveBuffer))
		return;

	waitReceiver();

	LOCK_INTERRUPTS;

	if(!currentReceiveBuffer->isOpen())
		disableFrameReceivedInterrupts();
	else
	{
		currentReceiveBuffer->handleStatus(usart->status());
		currentReceiveBuffer->receiveByte(usart->readFrame());
	}

	UNLOCK_INTERRUPTS;
}

void ActiveBufferedUSART::fillCurrentReceiveBuffer()
{
	disableFrameReceivedInterrupts();

	if(!isNull(currentReceiveBuffer))
		while(currentReceiveBuffer->isOpen())
			readOperation();


}

void ActiveBufferedUSART::setCurrentReceiveBuffer(ActiveReceiveBuffer * receiveBuffer)
{
	LOCK_INTERRUPTS;

	if(!isNull(receiveBuffer))
	{
		currentReceiveBuffer = receiveBuffer;

		currentReceiveBuffer->setObserver(this);
	}

	UNLOCK_INTERRUPTS;

}

void ActiveBufferedUSART::enableReceiver()
{
	LOCK_INTERRUPTS;

	if(!isNull(currentReceiveBuffer))
		usart->enableReceiver();

	UNLOCK_INTERRUPTS;

}

void ActiveBufferedUSART::enableFrameReceivedInterrupts()
{

	LOCK_INTERRUPTS;

	if(!isNull(currentReceiveBuffer))
		usart->enableFrameReceivedInterrupts();

	UNLOCK_INTERRUPTS;

}

ActiveReceiveBuffer * ActiveBufferedUSART::reclaimCurrentReceiveBuffer()
{

	ActiveReceiveBuffer * currentReceiveBuffer;

	LOCK_INTERRUPTS;

	currentReceiveBuffer = this->currentReceiveBuffer;

	releaseCurrentReceiveBuffer();

	UNLOCK_INTERRUPTS;

	return currentReceiveBuffer;
}


void ActiveBufferedUSART::releaseCurrentReceiveBuffer()
{
	LOCK_INTERRUPTS;

	disableFrameReceivedInterrupts();

	disableReceiver();


	if(!isNull(currentReceiveBuffer))
		currentReceiveBuffer->setObserver(NULL);

	currentReceiveBuffer = NULL;

	UNLOCK_INTERRUPTS;
}

void ActiveBufferedUSART::disableReceiver()
{
	LOCK_INTERRUPTS;
	usart->disableReceiver();
	UNLOCK_INTERRUPTS;
}

void ActiveBufferedUSART::disableFrameReceivedInterrupts()
{
	LOCK_INTERRUPTS;
	usart->disableFrameReceivedInterrupts();
	UNLOCK_INTERRUPTS;
}

void ActiveBufferedUSART::waitTransmitter()
{
	while(!usart->isTransmitterReady() && currentTransmitBuffer->isOpen());
}

void ActiveBufferedUSART::writeOperation()
{
	if(isNull(currentTransmitBuffer))
		return disableTransmitterReadyInterrupts();

	waitTransmitter();

	LOCK_INTERRUPTS;

	if(!currentTransmitBuffer->isOpen())
		disableTransmitterReadyInterrupts();
	else
		usart->writeFrame(currentTransmitBuffer->transmitByte());

	UNLOCK_INTERRUPTS;
}

void ActiveBufferedUSART::writeBaudSleepSafe()
{

	if(isNull(currentTransmitBuffer))
		return disableTransmitterReadyInterrupts();

	waitTransmitter();

	LOCK_INTERRUPTS;

	if(!currentTransmitBuffer->isOpen())
		disableTransmitterReadyInterrupts();
	else
	{
		usart->clearFrameTransmittedFlag();

		usart->writeFrame(currentTransmitBuffer->transmitByte());

		UNLOCK_INTERRUPTS;

		while(!usart->isFrameTransmitted());
	}

	UNLOCK_INTERRUPTS; // no harm in unlocking twice
}
void ActiveBufferedUSART::flushCurrentTransmitBuffer()
{
	disableTransmitterReadyInterrupts();

	if(!isNull(currentTransmitBuffer))
		while(currentTransmitBuffer->isOpen())
			writeOperation();

}

void ActiveBufferedUSART::setCurrentTransmitBuffer(ActiveTransmitBuffer * transmitBuffer)
{
	LOCK_INTERRUPTS;

	if(!isNull(transmitBuffer))
	{
		currentTransmitBuffer = transmitBuffer;
		currentTransmitBuffer->setObserver(this);
	}

	UNLOCK_INTERRUPTS;

}

void ActiveBufferedUSART::enableTransmitter()
{
	LOCK_INTERRUPTS;

	if(!isNull(currentTransmitBuffer))
		usart->enableTransmitter();

	UNLOCK_INTERRUPTS;

}

void ActiveBufferedUSART::enableTransmitterReadyInterrupts()
{
	LOCK_INTERRUPTS;

	if(!isNull(currentTransmitBuffer))
	{
		usart->enableTransmitterReadyInterrupts();
	}

	UNLOCK_INTERRUPTS;

}

ActiveTransmitBuffer * ActiveBufferedUSART::reclaimCurrentTransmitBuffer()
{

	ActiveTransmitBuffer * currentTransmitBuffer;

	LOCK_INTERRUPTS;

	currentTransmitBuffer = this->currentTransmitBuffer;

	releaseCurrentTransmitBuffer();

	UNLOCK_INTERRUPTS;

	return currentTransmitBuffer;
}


void ActiveBufferedUSART::releaseCurrentTransmitBuffer()
{
	LOCK_INTERRUPTS;

	disableTransmitterReadyInterrupts();

	disableTransmitter();

	if(!isNull(currentTransmitBuffer))
	{
		currentTransmitBuffer->setObserver(NULL);

		currentTransmitBuffer = NULL;
	}

	UNLOCK_INTERRUPTS;
}

void ActiveBufferedUSART::disableTransmitter()
{
	LOCK_INTERRUPTS;
	usart->disableTransmitter();
	UNLOCK_INTERRUPTS;
}


void ActiveBufferedUSART::disableTransmitterReadyInterrupts()
{
	LOCK_INTERRUPTS;
	usart->disableTransmitterReadyInterrupts();
	UNLOCK_INTERRUPTS;
}

}

}
